/*
Copyright (c) 2013 Mihail Volkov

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#ifndef QUICKSTART_UNIT_H_INCLUDED
#define QUICKSTART_UNIT_H_INCLUDED

#include "../server/htmd.h"

#ifndef DISABLE_HTMD

#include <map>
#include <deque>
#include <cstdio>

/// @brief Simple basic unit that can send and receive
/// strings to/from the front end. Strings are buffered
/// up to a given buffer size.
/// You can use this class as base for most units.
/// Most HtMdUnit interface is implemented and private-d,
/// but derived classes may still redefine some functions.
class HtMdQuickStartUnit: public HtMdUnit
{
	int mOutBufSize,mInBufSize;

	// outgoing messages stuff
	std::deque<std::string> mOutCommands;
	int mOutCommandsTotalSize,mOutCommandSofar;
	bool mOverflowWarningEmitted;

	// incoming stuff
	std::string mConsoleInputCommand;
	int mConsoleInputSofar,mConsoleInputTotal;
	bool mConsoleInputTruncated;

	bool mClientReady;

	// HtMdUnit implementation (privated)
	void inClientCommandBegin (int len);
	void inClientCommandChunk
		(const HtMdConstChunkDesc &chunk);
	void inClientCommandOk ();
	void inClientCommandCancelled ();
	int outUnitCommandChunk (HtMdChunkDesc &chunk);
	void outUnitCommandDone ();

protected:
	/// @brief If your unit handles onClientConnected, its
	/// implementation should include call to
	/// HtMdQuickStartUnit::onClientConnected
	void onClientConnected ();
	/// @brief If your unit handles onClientDisconnected, its
	/// implementation should include call to
	/// HtMdQuickStartUnit::onClientDisconnected
	void onClientDisconnected ();
	/// @brief If your unit handles onClientTurnIn, its
	/// implementation should include call to
	/// HtMdQuickStartUnit::onClientTurnIn
	void onClientTurnIn ();
	/// @brief If your unit handles onClientSuspended, its
	/// implementation should include call to
	/// HtMdQuickStartUnit::onClientSuspended
	void onClientSuspended ();

	/// @brief Construction of unit base. Specify buffers size in
	/// bytes (or specify 0 for unlimited buffer).
	HtMdQuickStartUnit
		(int sendBufferSize=0,int recvBufferSize=0);
	/// @brief Send a string to the client as a command. It will be
	/// delivered to unit's page's htmd.onUnitCommand () handler.
	/// Not guaranteed to be delivered until onClientReady or after
	/// onClientSuspended. Also, can be dropped if sending buffer
	/// overflows (see getSendOverflowWarningString).
	void sendString (const std::string &);
	/// @brief Callback to handle a string as a command from the
	/// client. The string is passed the same as given by unit's
	/// page's htmd.issueAppCommand () but can be truncated if it
	/// exceeds recv buffer size (overflowTruncated is true in this
	/// case).
	virtual void onReceiveString
		(const std::string &,bool overflowTruncated) = 0;
	/// @brief Return true if the unit's presentation window in the
	/// client is ready to accept sendString-s.
	bool isClientReady () { return mClientReady; }
	/// @brief Get the string to send to client when sending buffer
	/// is overflowed.
	virtual std::string getSentOverflowWarningString () = 0;
	/// @brief Emit sending buffer overflow string to the client,
	/// try to avoid repeating it more than necessary. This function
	/// is typically called automatically during sendString and you
	/// don't have to bother about this (except for what the client
	/// side should do about the overflow situation), but your code
	/// can invoke it directly instead of sendString if it thinks
	/// that the sending would result overflow for sure.
	void emitOutputOverflowWarning ();

public:
	~HtMdQuickStartUnit ();
};

/// @brief Simple basic file uploader that accepts a file and stores
/// it in a local app filesystem file (binary mode) with a given file
/// name. You can make inherited classes to make destination selection
/// more flexible.
class HtMdQuickFileUploader: public HtMdFileUploader
{
	FILE *mpFile;
	std::string mName,mFilter,mError;

protected:
	void inUploadBegin (const std::string &selectedName,int size);
	void inUploadChunk (const HtMdConstChunkDesc &chunk);
	void inUploadOk ();
	void inUploadCancelled ();

public:
	HtMdQuickFileUploader ();
	~HtMdQuickFileUploader ();

	/// @brief Allows to override initially specified name based on
	/// name of the file actually selected by user in the client.
	/// @param initialName Name you specified in beginUpload
	/// @param userName Name of the file selected by user in client's
	/// file selection dialog
	/// @return Final name of the local file where the uploaded file
	/// will be stored, default implementation returns initialName
	/// unchanged or userName if initialName is blank
	virtual std::string acceptIncomingName
		(const std::string &initialName,const std::string &userName)
	{ return initialName==""? userName : initialName; }

	/// @brief Begin request to the client to upload a file
	/// @param fileName Filename that you want to save the file
	/// under in the filesystem
	/// @param filter Filter to offer to the client, in Qt format
	/// ("mask1 mask2 ..." or "title1 (mask1);;title2 (mask2);;...",
	/// etc.), leave empty or omit to use default filter
	/// (the file name + all files)
	void beginUpload (const std::string &fileName,
		const std::string &filter="");

	/// @brief Return string description of error if the uploading
	/// ended with isFailed ()==true
	std::string getErrorString ();
};

/// @brief Simple basic file downloader that fetches a file with
/// a given file name from local app filesystem file (binary mode).
class HtMdQuickFileDownloader: public HtMdFileDownloader
{
	FILE *mpFile;
	std::string mName,mFilter,mError;

protected:
	void inDownloadBegin (const std::string &selectedName);
	void inDownloadChunk (HtMdChunkDesc chunk);
	void inDownloadOk ();
	void inDownloadCancelled ();

public:
	HtMdQuickFileDownloader ();
	~HtMdQuickFileDownloader ();

	/// @brief Begin request to the client to download a file
	/// @param fileName Name of the file that you want to load from
	/// the filesystem
	/// @param filter Filter to offer to the client, in Qt format
	/// ("mask1 mask2 ..." or "title1 (mask1);;title2 (mask2);;...",
	/// etc.), leave empty or omit to use default filter
	/// @return false if failed (no file, failed to open, etc.)
	bool beginDownload (const std::string &fileName,
		const std::string &filter="");

	/// @brief Return string description of error if the uploading
	/// ended with isFailed ()==true
	std::string getErrorString ();
};

#endif // DISABLE_HTMD

#endif // QUICKSTART_UNIT_H_INCLUDED
